package com.google.bitcoin.core;

import java.util.HashSet;
import java.util.Collection;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;


public class LiteTransaction implements java.io.Serializable
{
    public static final long serialVersionUID = 2L;
    private static final Logger log = LoggerFactory.getLogger(LiteTransaction.class);

    private transient Transaction tx_cache;
    private byte[] serialized_trans;
    private HashSet<Sha256Hash> blocks_seen;

    private Sha256Hash[] spent_by_list;

    public LiteTransaction(Transaction tx)
    {
        serialized_trans = tx.bitcoinSerialize();
        blocks_seen = new HashSet<Sha256Hash>();

        if (tx.getAppearsInHashes()!=null)
        {
            blocks_seen.addAll(tx.getAppearsInHashes());
        }

        spent_by_list = new Sha256Hash[tx.getOutputs().size()];

        int i=0;
        for(TransactionOutput out : tx.getOutputs())
        {
            if (out.getSpentBy()!=null)
            {
                spent_by_list[i] = out.getSpentBy().getParentTransaction().getHash();
            }
            i++;
        }

    }

    public synchronized Transaction getTx(NetworkParameters params)
    {
        Preconditions.checkNotNull(params);
        if (tx_cache == null)
        {
            try
            {
                tx_cache = new Transaction(params, serialized_trans);
                //reconstitute spent by, seen blocks

                int i=0;
                for(TransactionOutput out : tx_cache.getOutputs())
                {
                    //out.setParams(params);
                    if (spent_by_list[i] != null)
                    {
                        out.markAsSpent(null);
                        Preconditions.checkState(!out.isAvailableForSpending());
                        Preconditions.checkState(!tx_cache.getOutputs().get(i).isAvailableForSpending());

                        log.debug(tx_cache.getHash().toString()+ ":" + i + " - spent");
                        
                    }
                    else
                    {
                        out.markAsUnspent();
                        Preconditions.checkState(out.isAvailableForSpending());
                        Preconditions.checkState(tx_cache.getOutputs().get(i).isAvailableForSpending());
                     
                        log.debug(tx_cache.getHash().toString()+ ":" + i + " - unspent");
                    }
                    i++;
                }

                for(Sha256Hash appears : blocks_seen)
                {
                    tx_cache.addBlockAppearance(appears);
                }

            }
            catch(com.google.bitcoin.core.ProtocolException e)
            {
                log.error("Unable to deserialize transaction", e);
            }
        }
        return tx_cache;
    }

    public void markSpent(int out_idx, Sha256Hash spender)
    {
        spent_by_list[out_idx]=spender;
        tx_cache=null;
    }
    public boolean isSpent(int out_idx)
    {
        return (getSpentBy(out_idx) != null);
    }
    public Sha256Hash getSpentBy(int out_idx)
    {
        return spent_by_list[out_idx];
    }   

    public Collection<Sha256Hash> getBlocksSeen()
    {
        return blocks_seen;

    }
    public void addBlockAppearance(Sha256Hash block)
    {
        blocks_seen.add(block);
        tx_cache=null;
    }

    public boolean isAllSpent()
    {
        for(Sha256Hash s : spent_by_list)
        {
            if (s==null) return false;
        }
        return true;
    }


}
